/* 
 * File:   BinaryOperator.cpp
 * Author: Gab'z
 * 
 * Created on 31 mai 2012, 15:23
 */

#include "BinaryOperator.h"
#include "../../../Context.h"
#include "../../../memento/StackMemento.h"
#include "../../literal/Constant.h"
#include "../../literal/Expression.h"
#include "../../literal/Complex.h"

namespace calculator {

    BinaryOperator::BinaryOperator(const std::string name) : Operator(name) {
    }

    BinaryOperator::BinaryOperator(const BinaryOperator& orig) :
    Operator(orig._name) {
    }

    BinaryOperator::~BinaryOperator() {
    }

    std::string BinaryOperator::isExecutable() const {
        if (Context::getInstance()->getCurrentStack()->size() < 2) return "There must be at least 2 Constant in stack";
        const Constant* c1 = Context::getInstance()->getCurrentStack()->getConstant(0);
        const Constant* c2 = Context::getInstance()->getCurrentStack()->getConstant(1);
        const Complex* co1 = dynamic_cast<const Complex*> (c1);
        const Complex* co2 = dynamic_cast<const Complex*> (c2);
        if ((co1 != 0 || co2 != 0)
                && Context::getInstance()->getParameters()->getComplexMode() == false) return "Complex mode OFF";
        return "";
    }

    const Memento* BinaryOperator::createMemento() const throw (CommandException) {
        Stack* stack = new Stack();
        if (Context::getInstance()->getCurrentStack()->size() < 2) {
            std::ostringstream oss;
            oss << "Not enough Constant in current stack (";
            oss << Context::getInstance()->getCurrentStack()->size() << "/2)";
            throw CommandException(oss.str());
        }
        stack->push(Context::getInstance()->getCurrentStack()->getConstant(0));
        stack->push(Context::getInstance()->getCurrentStack()->getConstant(1));
        return new StackMemento(clone(), stack);
    }

    void BinaryOperator::eval(Stack* stack) const throw (CommandException) {
        if (stack->size() < 2) {
            std::ostringstream oss;
            oss << "Not enough Constant in current stack (";
            oss << Context::getInstance()->getCurrentStack()->size() << "/2)";
            throw CommandException(oss.str());
        }
        const Constant* c1 = stack->getConstant(0);
        const Constant* c2 = stack->getConstant(1);
        const Expression* exp1 = dynamic_cast<const Expression*> (c1);
        const Expression* exp2 = dynamic_cast<const Expression*> (c2);
        const Constant* constant;
        if (exp1 != 0 || exp2 != 0) {
            if (exp1 != 0 && exp2 != 0) {
                constant = new Expression(exp1, exp2, this);
            } else {
                constant = new Expression(c1, c2, this);
            }
        } else {
            const Number* n1 = dynamic_cast<const Number*> (c1);
            const Number* n2 = dynamic_cast<const Number*> (c2);
            try {
                constant = apply(n1, n2);
            } catch (CommandException& e) {
                throw;
            }
        }
        stack->pop();
        stack->pop();
        stack->push(constant);
        delete constant;
    }
}